// $Id:  $
/**************************************************************************
 * This file is property of and copyright by the ALICE HLT Project        *
 * ALICE Experiment at CERN, All rights reserved.                         *
 *                                                                        *
 * Primary Authors: Artur Szostak <artursz@iafrica.com>                   *
 *                  for The ALICE HLT Project.                            *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/// @file   AliHLTGlobalTriggerWrapper.cxx
/// @author Artur Szostak <artursz@iafrica.com>
/// @date   28 Oct 2009
/// @brief  Implementation of the AliHLTGlobalTriggerWrapper interface class.
///
/// The AliHLTGlobalTriggerWrapper class is used to interface with an interpreted
/// class deriving from AliHLTGlobalTrigger. This is used when the global trigger
/// component is using CINT to interpret the trigger logic. The wrapper is necessary
/// to be able to call interpreted code from compiled code.

#include "AliHLTGlobalTriggerWrapper.h"
#include "TArrayL64.h"
#include "TClass.h"
#include "TInterpreter.h"
#include "TCint.h"

ClassImp(AliHLTGlobalTriggerWrapper)


namespace
{
  /// Variable to store the error message if an error occured in CINT when interpreting code.
  TString gCINTErrorMessage = "";
  
  /**
   * This routine is the callback for the CINT interpreter when it finds a syntax error
   * in the source code generated by the AliHLTGlobalTriggerComponent class.
   */
  void AliHLTOnErrorInCINT(char* message)
  {
    gCINTErrorMessage += message;
  }

} // end of namespace


AliHLTGlobalTriggerWrapper::AliHLTGlobalTriggerWrapper(const char* classname) :
  AliHLTGlobalTrigger(),
  AliHLTLogging(),
  fClass(NULL),
  fObject(NULL),
  fFillFromMenuCall(),
  fNewEventCall(),
  fAddCall(),
  fCalculateTriggerDecisionCall(),
  fGetCountersCall(),
  fSetCountersCall(),
  fCallFailed(false)
{
  // The default constructor.
  
  fClass = TClass::GetClass(classname);
  if (fClass == NULL)
  {
    HLTError("Could not find class information for '%s'.", classname);
    return;
  }
  fFillFromMenuCall.InitWithPrototype(fClass, "FillFromMenu", "const AliHLTTriggerMenu&");
  if (not fFillFromMenuCall.IsValid())
  {
    HLTError("Could not initialise method call object for class '%s' and method FillFromMenu.", classname);
    return;
  }
  fNewEventCall.InitWithPrototype(fClass, "NewEvent", " ");  // Must have single whitespace in last parameter or we get a segfault in G__interpret_func.
  if (not fNewEventCall.IsValid())
  {
    HLTError("Could not initialise method call object for class '%s' and method NewEvent.", classname);
    return;
  }
  fAddCall.InitWithPrototype(fClass, "Add", "const TObject*, const AliHLTComponentDataType&, AliHLTUInt32_t");
  if (not fAddCall.IsValid())
  {
    HLTError("Could not initialise method call object for class '%s' and method Add.", classname);
    return;
  }
  fCalculateTriggerDecisionCall.InitWithPrototype(fClass, "CalculateTriggerDecision", "bool&, AliHLTTriggerDomain&, TString&");
  if (not fCalculateTriggerDecisionCall.IsValid())
  {
    HLTError("Could not initialise method call object for class '%s' and method CalculateTriggerDecision.", classname);
    return;
  }
  fGetCountersCall.InitWithPrototype(fClass, "GetCounters", " ");  // Must have single whitespace in last parameter or we get a segfault in G__interpret_func.
  if (not fGetCountersCall.IsValid())
  {
    HLTError("Could not initialise method call object for class '%s' and method GetCounters.", classname);
    return;
  }
  fSetCountersCall.InitWithPrototype(fClass, "SetCounters", "TArrayL64&");
  if (not fSetCountersCall.IsValid())
  {
    HLTError("Could not initialise method call object for class '%s' and method SetCounters.", classname);
    return;
  }
  fObject = fClass->New();
  if (fObject == NULL)
  {
    HLTError("Could not create a new object of type '%s'.", classname);
  }
  
  // The following is a workaround for casting void* to void (*)(), i.e. a pointer to object to
  // a pointer to function. Unfortunately the G__set_errmsgcallback routine interface is defined
  // using a pointer to object so this workaround is necessary.
  // We check that the two types are the same size. If they are not then such an operation is
  // unsafe on the platform on which we are running and will not be performed.
  union
  {
    void* fPtr;
    void (*fFuncPtr)(char*);
  };
  fFuncPtr = AliHLTOnErrorInCINT;
  if (sizeof(fPtr) == sizeof(fFuncPtr))
  {
    gInterpreter->SetErrmsgcallback(fPtr);
  }
  else
  {
    HLTWarning("On this platform a pointer to function and pointer to object are different sizes."
      " For this reason we cannot use the G__set_errmsgcallback CINT API call in a safe way so all"
      " error messages generated by CINT will not be captured or logged in the HLT system."
    );
  }
}


AliHLTGlobalTriggerWrapper::~AliHLTGlobalTriggerWrapper()
{
  // Default destructor.
  
  fClass->Destructor(fObject);
  G__set_errmsgcallback(NULL);
}


void AliHLTGlobalTriggerWrapper::FillFromMenu(const AliHLTTriggerMenu& menu)
{
  // Fills internal values from the trigger menu.
  
  fCallFailed = false;
  struct Params
  {
    const void* fMenu;
  } params;
  params.fMenu = &menu;
  fFillFromMenuCall.SetParamPtrs(&params, 1);
  gCINTErrorMessage = "";
  fFillFromMenuCall.Execute(fObject);
  if (gCINTErrorMessage != "")
  {
    fCallFailed = true;
    HLTError(gCINTErrorMessage.Data());
    HLTFatal("Error interpreting the code for class '%s' at line %d.", fClass->GetName(), G__lasterror_linenum());
  }
}


void AliHLTGlobalTriggerWrapper::NewEvent()
{
  // Clears the internal buffers for a new event.

  fCallFailed = false;
  gCINTErrorMessage = "";
  fNewEventCall.Execute(fObject);
  if (gCINTErrorMessage != "")
  {
    fCallFailed = true;
    HLTError(gCINTErrorMessage.Data());
    HLTFatal("Error interpreting the code for class '%s' at line %d.", fClass->GetName(), G__lasterror_linenum());
  }
}


void AliHLTGlobalTriggerWrapper::Add(
      const TObject* object, const AliHLTComponentDataType& type,
      AliHLTUInt32_t spec
    )
{
  // Adds parameters from the object to the internal buffers and variables.
  
  fCallFailed = false;
  struct Params
  {
    const void* fObj;
    const void* fType;
    long fSpec;
  } params;
  params.fObj = object;
  params.fType = &type;
  params.fSpec = spec;
  fAddCall.SetParamPtrs(&params, 3);
  gCINTErrorMessage = "";
  fAddCall.Execute(fObject);
  if (gCINTErrorMessage != "")
  {
    fCallFailed = true;
    HLTError(gCINTErrorMessage.Data());
    HLTFatal("Error interpreting the code for class '%s' at line %d.", fClass->GetName(), G__lasterror_linenum());
  }
}


bool AliHLTGlobalTriggerWrapper::CalculateTriggerDecision(bool& triggerResult, AliHLTTriggerDomain& domain, TString& description)
{
  // Calculates the global trigger decision.

  fCallFailed = false;
  struct Params
  {
    const void* fResult;
    const void* fDomain;
    const void* fDesc;
  } params;
  params.fResult = &triggerResult;
  params.fDomain = &domain;
  params.fDesc = &description;
  fCalculateTriggerDecisionCall.SetParamPtrs(&params, 3);
  Long_t retval;
  gCINTErrorMessage = "";
  fCalculateTriggerDecisionCall.Execute(fObject, retval);
  if (gCINTErrorMessage != "")
  {
    fCallFailed = true;
    HLTError(gCINTErrorMessage.Data());
    HLTFatal("Error interpreting the code for class '%s' at line %d.", fClass->GetName(), G__lasterror_linenum());
    return false;
  }
  return bool(retval);
}


const TArrayL64& AliHLTGlobalTriggerWrapper::GetCounters() const
{
  // Returns the internal counter array.

  Long_t retval = 0x0;
  gCINTErrorMessage = "";
  fGetCountersCall.Execute(fObject, retval);
  if (gCINTErrorMessage != "")
  {
    fCallFailed = true;
    HLTError(gCINTErrorMessage.Data());
    HLTFatal("Error interpreting the code for class '%s' at line %d.", fClass->GetName(), G__lasterror_linenum());
  }
  static const TArrayL64 emptyArray;
  const TArrayL64* ptr = &emptyArray; // Make sure we do not return a NULL pointer.
  if (retval != 0x0) ptr = reinterpret_cast<const TArrayL64*>(retval);
  return *ptr;
}


void AliHLTGlobalTriggerWrapper::SetCounters(const TArrayL64& counters)
{
  // Fills the internal counter array with new values.
  
  fCallFailed = false;
  struct Params
  {
    const void* fCounters;
  } params;
  params.fCounters = &counters;
  fSetCountersCall.SetParamPtrs(&params, 1);
  gCINTErrorMessage = "";
  fSetCountersCall.Execute(fObject);
  if (gCINTErrorMessage != "")
  {
    fCallFailed = true;
    HLTError(gCINTErrorMessage.Data());
    HLTFatal("Error interpreting the code for class '%s' at line %d.", fClass->GetName(), G__lasterror_linenum());
  }
}


bool AliHLTGlobalTriggerWrapper::IsValid() const
{
  // Checks if the wrapper class was initialised correctly.
  
  return fClass != NULL and fObject != NULL and fFillFromMenuCall.IsValid()
    and fNewEventCall.IsValid() and fAddCall.IsValid()
    and fCalculateTriggerDecisionCall.IsValid();
}

